import streamlit as st
import pandas as pd
import plotly.express as px
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime

# 设置matplotlib中文显示
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

# 设置页面配置为宽屏模式
st.set_page_config(layout="wide")

# 设置页面标题（更新为更通用的名称）
st.title('📊 TableGrapher')

# 上传文件（支持csv和txt格式）
uploaded_file = st.file_uploader('选择CSV或TXT文件', type=['csv', 'txt'])

if uploaded_file is not None:
    # 读取文件（pandas可以自动处理csv和txt格式）
        
    try:
        # 尝试以utf-8编码读取csv文件
        df = pd.read_csv(uploaded_file)
    except UnicodeDecodeError:
        # 若utf-8解码失败，尝试以gbk编码读取
        uploaded_file.seek(0)  # 重置文件指针
        try:
            df = pd.read_csv(uploaded_file, encoding='gbk')
        except Exception:
            st.error('文件解码失败，请确保文件编码为UTF-8或GBK')
            st.stop()
    except Exception:
        # 尝试以txt格式读取文件
        uploaded_file.seek(0)  # 重置文件指针
        try:
            df = pd.read_csv(uploaded_file, sep='	', encoding='utf-8')
        except UnicodeDecodeError:
            # 若utf-8解码失败，尝试以gbk编码读取txt文件
            uploaded_file.seek(0)  # 重置文件指针
            try:
                df = pd.read_csv(uploaded_file, sep='	', encoding='gbk')
            except Exception:
                st.error('文件解码失败，请确保文件编码为UTF-8或GBK，且为CSV或TXT格式')
                st.stop()

    st.write('数据预览:')
    num_of_rows = st.number_input('预览行数', min_value=1, max_value=200, value=10)
    st.dataframe(df.head(num_of_rows))

    st.write('数据描述:')
    st.dataframe(df.describe())

    # 自动识别时间列
    date_columns = []
    for col in df.columns:
        # 尝试将列转换为日期时间
        try:
            pd.to_datetime(df[col])
            date_columns.append(col)
        except:
            pass

    # 让用户选择时间列
    if len(date_columns) > 0:
        date_col = st.selectbox('选择时间列', date_columns)
        df[date_col] = pd.to_datetime(df[date_col])
        
        # 添加时间范围筛选功能
        st.subheader('时间范围筛选')
        min_date = df[date_col].min()
        max_date = df[date_col].max()
        start_date, end_date = st.date_input(
            '选择时间范围',
            value=[min_date, max_date],
            min_value=min_date,
            max_value=max_date
        )
        
        # 过滤数据
        mask = (df[date_col].dt.date >= start_date) & (df[date_col].dt.date <= end_date)
        filtered_df = df[mask]
        
        # 让用户选择要可视化的其他列
        other_cols = [col for col in df.columns if col != date_col]
        
        # 添加关键词搜索功能
        search_term = st.text_input('搜索列名', '')
        if search_term:
            filtered_cols = [col for col in other_cols if search_term.lower() in col.lower()]
        else:
            filtered_cols = other_cols
        
        selected_cols = st.multiselect('选择要可视化的列', filtered_cols)

        # 绘制时间序列图
        if len(selected_cols) > 0:
            # 让用户选择可视化库
            viz_lib = st.radio(
                '选择可视化库',
                ('Plotly', 'Matplotlib'),
                index=0,
                horizontal=True
            )
            if viz_lib == 'Plotly':
            # Plotly图表
                st.subheader('Plotly时间序列图')
                fig = px.line(filtered_df, x=date_col, y=selected_cols, title='时间序列可视化')
                fig.update_layout(
                    xaxis_title=date_col,
                    yaxis_title='值',
                    font=dict(family='SimHei, Arial, sans-serif'),
                    legend_title_text='列名',
                    height=600  
                )
                st.plotly_chart(fig, use_container_width=True)
            else:
                # 添加Matplotlib图表
                st.subheader('Matplotlib时间序列图')
                fig, ax = plt.subplots(figsize=(12, 6))
                for col in selected_cols:
                    sns.lineplot(x=filtered_df[date_col], y=filtered_df[col], ax=ax, label=col)
                plt.xlabel(date_col)
                plt.ylabel('值')
                plt.legend()
                plt.tight_layout()
                st.pyplot(fig)

            # 添加直方图展示
            st.subheader('数据分布直方图')
            
            # 让用户选择可视化库
            viz_lib = st.radio(
                '选择可视化库',
                ('Plotly', 'Matplotlib'),
                index=0,
                horizontal=True,
                key='viz_lib_histogram'
            )
            
            for col in selected_cols:
                if viz_lib == 'Plotly':
                    # Plotly直方图
                    fig = px.histogram(filtered_df, x=col, title=f'{col}的分布')
                    fig.update_layout(
                        xaxis_title=col,
                        yaxis_title='频数',
                        font=dict(family='SimHei, Arial, sans-serif'),
                        height=400
                    )
                    st.plotly_chart(fig, use_container_width=True)
                else:
                    # Matplotlib直方图
                    fig, ax = plt.subplots(figsize=(10, 4))
                    sns.histplot(filtered_df[col], kde=True, ax=ax)
                    plt.title(f'{col}的分布')
                    plt.xlabel(col)
                    plt.ylabel('频数')
                    plt.tight_layout()
                    st.pyplot(fig)
                     
            # 添加箱线图展示
            st.subheader('数据分布箱线图')
            
            # 让用户选择可视化库
            viz_lib = st.radio(
                '选择可视化库',
                ('Plotly', 'Matplotlib'),
                index=0,
                horizontal=True,
                key='viz_lib_boxplot'
            )
            
            if viz_lib == 'Plotly':
                # Plotly箱线图
                fig = px.box(filtered_df, y=selected_cols, title='箱线图可视化')
                fig.update_layout(
                    yaxis_title='值',
                    font=dict(family='SimHei, Arial, sans-serif'),
                    height=600
                )
                st.plotly_chart(fig, use_container_width=True)
            else:
                # Matplotlib箱线图
                fig, ax = plt.subplots(figsize=(5, 10))
                sns.boxplot(data=filtered_df[selected_cols], ax=ax)
                plt.title('箱线图可视化')
                plt.ylabel('值')
                plt.xticks(rotation=45)
                plt.tight_layout()
                st.pyplot(fig)
            
            # 添加散点图展示
            st.subheader('二维散点图')
            
            # 为散点图设计独立的列名选择框
            scatter_cols = st.multiselect('选择用于散点图的列', filtered_cols, key='scatter_cols')
            
            # 检查是否至少选择了两列数据
            if len(scatter_cols) >= 2:
                # 让用户选择横轴和纵轴
                x_col = st.selectbox('选择横轴数据', scatter_cols, key='x_axis')
                y_col = st.selectbox('选择纵轴数据', [col for col in scatter_cols if col != x_col], key='y_axis')
                
                # 让用户选择可视化库
                viz_lib = st.radio(
                    '选择可视化库',
                    ('Plotly', 'Matplotlib'),
                    index=0,
                    horizontal=True,
                    key='viz_lib_scatter'
                )
                
                if viz_lib == 'Plotly':
                    # Plotly散点图
                    fig = px.scatter(filtered_df, x=x_col, y=y_col, title=f'{x_col} vs {y_col} 散点图')
                    fig.update_layout(
                        xaxis_title=x_col,
                        yaxis_title=y_col,
                        font=dict(family='SimHei, Arial, sans-serif'),
                        height=600
                    )
                    st.plotly_chart(fig, use_container_width=True)
                else:
                    # Matplotlib散点图
                    fig, ax = plt.subplots(figsize=(10, 6))
                    sns.scatterplot(data=filtered_df, x=x_col, y=y_col, ax=ax)
                    plt.title(f'{x_col} vs {y_col} 散点图')
                    plt.xlabel(x_col)
                    plt.ylabel(y_col)
                    plt.tight_layout()
                    st.pyplot(fig)
            else:
                st.warning('请至少选择两列数据以绘制散点图')
        else:
            st.warning('请至少选择一列进行可视化')
    else:
        st.warning('未找到可识别的时间列，请检查您的文件')
else:
    st.info('请上传CSV或TXT文件以开始分析')

#streamlit run TableGrapher.py